package com.zz.cc.owner.app;

import java.io.IOException;
import java.util.Collection;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.DecodeHintType;
import com.google.zxing.Result;
import com.google.zxing.ResultMetadataType;
import com.google.zxing.ResultPoint;
import com.google.zxing.client.android.Action;
import com.google.zxing.client.android.AmbientLightManager;
import com.google.zxing.client.android.BeepManager;
import com.google.zxing.client.android.CaptureActivity;
import com.google.zxing.client.android.DecodeActivityHandler;
import com.google.zxing.client.android.DecodeFormatManager;
import com.google.zxing.client.android.DecodeHintManager;
import com.google.zxing.client.android.InactivityTimer;
import com.google.zxing.client.android.IntentSource;
import com.google.zxing.client.android.Intents;
import com.google.zxing.client.android.ViewfinderView;
import com.google.zxing.client.android.camera.CameraManager;
import com.zz.cc.owner.R;
import com.zz.common.app.BaseActivity;
import com.zz.common.utils.ZLog;

public class QrCodeScanActivity extends BaseActivity implements Callback, DecodeActivityHandler.IHandleActivity{
	  private static final String TAG = CaptureActivity.class.getSimpleName();

	  private static final Set<ResultMetadataType> DISPLAYABLE_METADATA_TYPES =
	      EnumSet.of(ResultMetadataType.ISSUE_NUMBER,
	                 ResultMetadataType.SUGGESTED_PRICE,
	                 ResultMetadataType.ERROR_CORRECTION_LEVEL,
	                 ResultMetadataType.POSSIBLE_COUNTRY);

	  private CameraManager cameraManager;
	  private DecodeActivityHandler handler;
	  private Result savedResultToShow;
	  private ViewfinderView viewfinderView;
	  private Result lastResult;
	  private boolean hasSurface;

	  private Collection<BarcodeFormat> decodeFormats;
	  private Map<DecodeHintType,?> decodeHints;
	  private String characterSet;
	  
	  private InactivityTimer inactivityTimer;
	  private BeepManager beepManager;
	  private AmbientLightManager ambientLightManager;

	  @Override
	  public ViewfinderView getViewfinderView() {
	    return viewfinderView;
	  }

	  @Override
	  public Handler getHandler() {
	    return handler;
	  }

	  @Override
	  public CameraManager getCameraManager() {
	    return cameraManager;
	  }

	  @Override
	  public void onCreate(Bundle icicle) {
	    super.onCreate(icicle);

	    Window window = getWindow();
	    window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

	    setContentView(R.layout.activity_qrcode_scan);
	    
	    hasSurface = false;
	    inactivityTimer = new InactivityTimer(this);
	    beepManager = new BeepManager(this, getBeepId());
	    ambientLightManager = new AmbientLightManager(this);
	  }
	  
	  protected int getBeepId() {
		  return R.raw.beep;
	  }
	  
	  protected int getViewFinderViewId() {
		  return R.id.qrcode_scan_viewfinder_view;
	  }
	  
	  protected int getSurfaceViewId() {
		  return R.id.qrcode_scan_preview_view;
	  }
	  
	  @Override
	public void onResume() {
	    super.onResume();

	    // CameraManager must be initialized here, not in onCreate(). This is necessary because we don't
	    // want to open the camera driver and measure the screen size if we're going to show the help on
	    // first launch. That led to bugs where the scanning rectangle was the wrong size and partially
	    // off screen.
	    cameraManager = new CameraManager(getApplication());

	    viewfinderView = (ViewfinderView) findViewById(getViewFinderViewId());
	    viewfinderView.setCameraManager(cameraManager);

	    handler = null;
	    lastResult = null;

	    SurfaceView surfaceView = (SurfaceView) findViewById(getSurfaceViewId());
	    SurfaceHolder surfaceHolder = surfaceView.getHolder();
	    if (hasSurface) {
	      // The activity was paused but not stopped, so the surface still exists. Therefore
	      // surfaceCreated() won't be called, so init the camera here.
	      initCamera(surfaceHolder);
	    } else {
	      // Install the callback and wait for surfaceCreated() to init the camera.
	      surfaceHolder.addCallback(this);
	      surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	    }

	    beepManager.updateConfig();
	    ambientLightManager.start(cameraManager);

	    inactivityTimer.onResume();
	  }

	  @Override
	  public void onPause() {
	    if (handler != null) {
	      handler.quitSynchronously();
	      handler = null;
	    }
	    inactivityTimer.onPause();
	    ambientLightManager.stop();
	    cameraManager.closeDriver();
	    if (!hasSurface) {
	      SurfaceView surfaceView = (SurfaceView) findViewById(getSurfaceViewId());
	      SurfaceHolder surfaceHolder = surfaceView.getHolder();
	      surfaceHolder.removeCallback(this);
	    }
	    super.onPause();
	  }

	  @Override
	  public void onDestroy() {
	    inactivityTimer.shutdown();
	    super.onDestroy();
	  }

	  @Override
	  public boolean onKeyDown(int keyCode, KeyEvent event) {
	    switch (keyCode) {
	      case KeyEvent.KEYCODE_BACK:
	        break;
	      case KeyEvent.KEYCODE_FOCUS:
	      case KeyEvent.KEYCODE_CAMERA:
	        // Handle these events so they don't launch the Camera app
	        return true;
	      // Use volume up/down to turn on light
	      case KeyEvent.KEYCODE_VOLUME_DOWN:
	        cameraManager.setTorch(false);
	        return true;
	      case KeyEvent.KEYCODE_VOLUME_UP:
	        cameraManager.setTorch(true);
	        return true;
	    }
	    return super.onKeyDown(keyCode, event);
	  }

	  @Override
	  public void onActivityResult(int requestCode, int resultCode, Intent intent) {
	    super.onActivityResult(requestCode, resultCode, intent);
	  }

	  private void decodeOrStoreSavedBitmap(Bitmap bitmap, Result result) {
	    // Bitmap isn't used yet -- will be used soon
	    if (handler == null) {
	      savedResultToShow = result;
	    } else {
	      if (result != null) {
	        savedResultToShow = result;
	      }
	      if (savedResultToShow != null) {
	        Message message = Message.obtain(handler, Action.ACTION_DECODE, savedResultToShow);
	        handler.sendMessage(message);
	      }
	      savedResultToShow = null;
	    }
	  }

	  @Override
	  public void surfaceCreated(SurfaceHolder holder) {
	    if (holder == null) {
	      Log.e(TAG, "*** WARNING *** surfaceCreated() gave us a null surface!");
	    }
	    if (!hasSurface) {
	      hasSurface = true;
	      initCamera(holder);
	    }
	  }

	  @Override
	  public void surfaceDestroyed(SurfaceHolder holder) {
	    hasSurface = false;
	  }

	  @Override
	  public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

	  }

	  /**
	   * A valid barcode has been found, so give an indication of success and show the results.
	   *
	   * @param rawResult The contents of the barcode.
	   * @param scaleFactor amount by which thumbnail was scaled
	   * @param barcode   A greyscale bitmap of the camera data which was decoded.
	   */
	  public void handleDecode(Result rawResult, Bitmap barcode, float scaleFactor) {
	    inactivityTimer.onActivity();
	    lastResult = rawResult;

	    // Then not from history, so beep/vibrate and we have an image to draw on
	    beepManager.playBeepSoundAndVibrate();
	    drawResultPoints(barcode, scaleFactor, rawResult);

        handleDecodeExternally(rawResult, lastResult, barcode);
	  }

	  /**
	   * Superimpose a line for 1D or dots for 2D to highlight the key features of the barcode.
	   *
	   * @param barcode   A bitmap of the captured image.
	   * @param scaleFactor amount by which thumbnail was scaled
	   * @param rawResult The decoded results which contains the points to draw.
	   */
	  private void drawResultPoints(Bitmap barcode, float scaleFactor, Result rawResult) {
	    ResultPoint[] points = rawResult.getResultPoints();
	    if (points != null && points.length > 0) {
	      Canvas canvas = new Canvas(barcode);
	      Paint paint = new Paint();
	      paint.setColor(Color.RED);
	      if (points.length == 2) {
	        paint.setStrokeWidth(4.0f);
	        drawLine(canvas, paint, points[0], points[1], scaleFactor);
	      } else if (points.length == 4 &&
	                 (rawResult.getBarcodeFormat() == BarcodeFormat.UPC_A ||
	                  rawResult.getBarcodeFormat() == BarcodeFormat.EAN_13)) {
	        // Hacky special case -- draw two lines, for the barcode and metadata
	        drawLine(canvas, paint, points[0], points[1], scaleFactor);
	        drawLine(canvas, paint, points[2], points[3], scaleFactor);
	      } else {
	        paint.setStrokeWidth(10.0f);
	        for (ResultPoint point : points) {
	          canvas.drawPoint(scaleFactor * point.getX(), scaleFactor * point.getY(), paint);
	        }
	      }
	    }
	  }

	  private static void drawLine(Canvas canvas, Paint paint, ResultPoint a, ResultPoint b, float scaleFactor) {
	    if (a != null && b != null) {
	      canvas.drawLine(scaleFactor * a.getX(), 
	                      scaleFactor * a.getY(), 
	                      scaleFactor * b.getX(), 
	                      scaleFactor * b.getY(), 
	                      paint);
	    }
	  }

	  // Briefly show the contents of the barcode, then handle the result outside Barcode Scanner.
	  private void handleDecodeExternally(Result rawResult, Result result, Bitmap barcode) {
		  String resultStr = rawResult.getText();
		  ZLog.d(TAG, "handleDecodeExternally: " + resultStr);
		  Intent intent = new Intent(this, QrCodeResultActivity.class);
		  intent.putExtra(QrCodeResultActivity.INDEX_QR_CODE, resultStr);
		  startActivity(intent);
		  finish();
		  overridePendingTransition(R.anim.activity_slide_right_in, R.anim.activity_slide_left_out);
	  }

	  private void initCamera(SurfaceHolder surfaceHolder) {
	    if (surfaceHolder == null) {
	      throw new IllegalStateException("No SurfaceHolder provided");
	    }
	    if (cameraManager.isOpen()) {
	      Log.w(TAG, "initCamera() while already open -- late SurfaceView callback?");
	      return;
	    }
	    try {
	      cameraManager.openDriver(surfaceHolder);
	      // Creating the handler starts the preview, which can also throw a RuntimeException.
	      if (handler == null) {
	        handler = new DecodeActivityHandler(this, decodeFormats, decodeHints, characterSet, cameraManager);
	      }
	      decodeOrStoreSavedBitmap(null, null);
	    } catch (IOException ioe) {
	      Log.w(TAG, ioe);
	      finish();
	    } catch (RuntimeException e) {
	      // Barcode Scanner has seen crashes in the wild of this variety:
	      // java.?lang.?RuntimeException: Fail to connect to camera service
	      Log.w(TAG, "Unexpected error initializing camera", e);
	      finish();
	    }
	  }

	  public void restartPreviewAfterDelay(long delayMS) {
	    if (handler != null) {
	      handler.sendEmptyMessageDelayed(Action.ACTION_RESTART_PREVIEW, delayMS);
	    }
	  }


	  public void drawViewfinder() {
	    viewfinderView.drawViewfinder();
	  }
}
